/****************************************************************************
**  Tite Interactive Text Engine (TiTE)
**  Copyright (C) 2010  Eisoft Group
**
**  This file is part of TiTE.
**
**  TiTE is free software: you can redistribute it and/or modify
**  it under the terms of the GNU General Public License as published by
**  the Free Software Foundation, either version 3 of the License, or
**  (at your option) any later version.
**
**  TiTE is distributed in the hope that it will be useful,
**  but WITHOUT ANY WARRANTY; without even the implied warranty of
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**  GNU General Public License for more details.
**
**  You should have received a copy of the GNU General Public License
**  along with TiTE.  If not, see <http://www.gnu.org/licenses/>.
****************************************************************************/

/*!
 * \file singleton.hpp
 *
 * \date 01/10/2011
 */

#ifndef TITE_CORE_SINGLETON_HPP
#define TITE_CORE_SINGLETON_HPP

#include <cassert>

#include <tite/core/singleton/detail/singleton_policies.hpp>

/*!
 * \namespace tite
 *
 * \brief The TiTE namespace.
 */
namespace tite
{
    /*!
     * \namespace core
     *
     * \brief The TiTE core namespace.
     */
    namespace core
    {
        /*!
         * \class singleton singleton.hpp
         *
         * \brief A policy-based templated singleton class.
         *
         * \author Jason Omahen
         * \version 0.02, 12/18/2010
         */
        template<
            typename T,
            priority_levels::PriorityLevel DestructionPriority = priority_levels::High,
            template <typename> class ThreadingPolicy = singleton_policies::single_threaded,
            template <typename> class LifetimePolicy = singleton_policies::managed_lifetime,
            template<
                typename,
                template <typename> class
            > class AllocationPolicy = singleton_policies::default_dynamic_allocator
        >
        class singleton : private non_copyable
        {
        public:
            typedef typename ThreadingPolicy<T>::instance_ptr_t instance_ptr_t;

            static instance_ptr_t GetInstance(void);
            static T& GetInstanceRef(void);

        private:
            static void _destroy(void);

            // Prevent explicit object construction
            singleton(void);
            ~singleton(void);

            // Attributes
            static instance_ptr_t m_pInstance;
            static typename ThreadingPolicy<T>::Mutex m_mutex;
            static bool m_bDestroyed;
        };

        //!
        // singleton<> template implementation
        //
        /**
         * Static data initialization.
         */
        template<
            typename T,
            priority_levels::PriorityLevel DestructionPriority,
            template <typename> class ThreadingPolicy,
            template <typename> class LifetimePolicy,
            template<
                typename,
                template <typename> class
            > class AllocationPolicy
        >
        typename singleton<T, DestructionPriority, ThreadingPolicy, LifetimePolicy, AllocationPolicy>::instance_ptr_t
        singleton<T, DestructionPriority, ThreadingPolicy, LifetimePolicy, AllocationPolicy>::m_pInstance = nullptr;

        template<
            typename T,
            priority_levels::PriorityLevel DestructionPriority,
            template <typename> class ThreadingPolicy,
            template <typename> class LifetimePolicy,
            template<
                typename,
                template <typename> class
            > class AllocationPolicy
        >
        typename ThreadingPolicy<T>::Mutex 
        singleton<T, DestructionPriority, ThreadingPolicy, LifetimePolicy, AllocationPolicy>::m_mutex;

        template<
            typename T,
            priority_levels::PriorityLevel DestructionPriority,
            template <typename> class ThreadingPolicy,
            template <typename> class LifetimePolicy,
            template<
                typename,
                template <typename> class
            > class AllocationPolicy
        >
        bool 
        singleton<T, DestructionPriority, ThreadingPolicy, LifetimePolicy, AllocationPolicy>::m_bDestroyed = false;

        /*!
         * \brief Retrieves the current--and only--instance of the singleton.
         *
         * \return Singleton instance pointer.
         */
        template<
            typename T,
            priority_levels::PriorityLevel DestructionPriority,
            template <typename> class ThreadingPolicy,
            template <typename> class LifetimePolicy,
            template<
                typename,
                template <typename> class
            > class AllocationPolicy
        >
        typename singleton<T, DestructionPriority, ThreadingPolicy, LifetimePolicy, AllocationPolicy>::instance_ptr_t 
        singleton<T, DestructionPriority, ThreadingPolicy, LifetimePolicy, AllocationPolicy>::GetInstance(void)
        {
            // Double-checked locking pattern
            if (!m_pInstance)
            {
                typename ThreadingPolicy<T>::ScopedThreadLock scopedLock(m_mutex);

                if (!m_pInstance)
                {
                    if (m_bDestroyed)
                    {
                        m_bDestroyed = false;
                        LifetimePolicy<T>::DeadReferenceDetected();
                    }

                    m_pInstance = AllocationPolicy<T, ThreadingPolicy>::Create();
                    LifetimePolicy<T>::RegisterDeleter<DestructionPriority>(&_destroy);
                }
            }
            return m_pInstance;
        }

        /*!
         * \brief Retrieves the current--and only--instance of the singleton.
         *
         * \return Singleton instance.
         */
        template<
            typename T,
            priority_levels::PriorityLevel DestructionPriority,
            template <typename> class ThreadingPolicy,
            template <typename> class LifetimePolicy,
            template<
                typename,
                template <typename> class
            > class AllocationPolicy
        >
        T& singleton<T, DestructionPriority, ThreadingPolicy, LifetimePolicy, AllocationPolicy>::GetInstanceRef(void)
        {
            return *GetInstance();
        }

        /*!
         * \brief Performs necessary procedures to free memory used by singleton, depending
         *        on the AllocationPolicy.
         */
        template<
            typename T,
            priority_levels::PriorityLevel DestructionPriority,
            template <typename> class ThreadingPolicy,
            template <typename> class LifetimePolicy,
            template<
                typename,
                template <typename> class
            > class AllocationPolicy
        >
        void singleton<T, DestructionPriority, ThreadingPolicy, LifetimePolicy, AllocationPolicy>::_destroy(void)
        {
            assert(!m_bDestroyed && "Singleton already destroyed.");

            AllocationPolicy<T, ThreadingPolicy>::Destroy(&m_pInstance);
            m_bDestroyed = true;
        }
    }// End of core namespace
}// End of tite namespace

#endif /* TITE_CORE_SINGLETON_HPP */